﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SRF.Data.Cfg
{
    /// <summary>
    /// 配置
    /// </summary>
    public class Configuration
    {
        static Configuration _default;
        static Action<string> _writeLog;

        public static Configuration Default { get { return _default; } }

        public Configuration()
        {
            _default = this;
        }

        public Configuration Configure()
        {
            ClassMappings = new List<ClassInfo>();
            Queries = new List<QueryInfo>();
            Properties = new Dictionary();
            Connections = new Dictionary<string, ConnectionInfo>();

            InitConfig();

            return this;
        }
        /// <summary>
        /// 根据配置文件获取配置信息
        /// </summary>
        /// <param name="configFile">配置文件</param>
        /// <returns></returns>
        public Configuration Configure(string configFile)
        {
            ClassMappings = new List<ClassInfo>();
            Queries = new List<QueryInfo>();
            Properties = new Dictionary();
            Connections = new Dictionary<string, ConnectionInfo>();

            _configFile = configFile;
            LoadConfig(configFile);
            //加载映射文件
            string mappingdir;
            if (Properties.Keys.Contains("mappingdir") == true)
            {
                mappingdir = Properties["mappingdir"];
                mappingdir = GetAbsolutePath(configFile.Substring(0, configFile.LastIndexOf("\\")), mappingdir);
            }
            else
            {
                mappingdir = configFile.Substring(0, configFile.LastIndexOf("\\"));
            }
            var files = System.IO.Directory.GetFiles(mappingdir, "*.map.xml", System.IO.SearchOption.AllDirectories);
            foreach (var file in files)
            {
                LoadMapping(file);
            }

            InitConfig();

            return this;
        }

        public DBHelperBase GetDBHelper()
        {
            DBHelperBase dbhelper = new DBHelper((DatabaseType)Enum.Parse(typeof(DatabaseType), Properties["dbtype"], true), Properties["connectionstring"]);
            dbhelper.ClassMappings = ClassMappings;
            dbhelper.Queries = Queries;
            return dbhelper;
        }

        public static Action<string> WriteLog
        {
            get { return _writeLog; }
            set
            {
                _writeLog = value;
            }
        }

        #region 属性

        string _configFile;
        /// <summary>
        /// 配置文件
        /// </summary>
        public string ConfigFile { get { return _configFile; } }

        /// <summary>
        /// 
        /// </summary>
        public ICollection<Cfg.ClassInfo> ClassMappings { get; set; }

        /// <summary>
        /// 自定义查询语句
        /// </summary>
        public ICollection<Cfg.QueryInfo> Queries { get; set; }

        /// <summary>
        /// 参数列表
        /// </summary>
        public Dictionary Properties { get; set; }

        /// <summary>
        /// 连接字符串
        /// </summary>
        public Dictionary<string, ConnectionInfo> Connections { get; set; }

        public T Get<T>(string key)
        {
            return Properties.Get<T>(key);
        }

        #endregion

        #region

        void LoadConfig(string configFile)
        {
            try
            {
                System.Xml.Linq.XElement root = System.Xml.Linq.XElement.Load(configFile);
                //属性
                foreach (var p in from e in root.Elements(root.Name.Namespace + "property") select e)
                {
                    Properties.Add(p.Attribute("name").Value.ToLower(), p.Value);
                }
                Query.EnumAsInt = Properties.Get<bool>("enumasint", false);
                Impl.Database.WriteLog = Properties.Get<bool>("logging", true) ? _writeLog : null;
                //数据库连接
                foreach (var c in from e in root.Elements(root.Name.Namespace + "connection") select e)
                {
                    Connections.Add(c.Attribute("name").Value.ToLower(),
                        new ConnectionInfo()
                        {
                            DbType = (DatabaseType)Enum.Parse(typeof(DatabaseType), c.Attribute("dbtype").Value, true),
                            ConnectionString = c.Value.Trim()
                        });
                }
            }
            catch
            {
                throw new System.Xml.XmlException("配置文件读取错误");
            }
        }
        /// <summary>
        /// 加载配置文件
        /// </summary>
        /// <param name="mappingFile"></param>
        void LoadMapping(string mappingFile)
        {
            IList<ClassInfo> list = new List<ClassInfo>();
            System.Xml.Linq.XElement root = System.Xml.Linq.XElement.Load(mappingFile);
            var xmlns = root.Name.Namespace;
            //加载实体映射信息 
            foreach (var c in from e in root.Elements(xmlns + "class") select e)
            {
                var item = new ClassInfo();
                //添加类信息
                item.Name = c.Attribute("name").Value;
                item.Table = c.Attribute("table").Value;
                item.AllMapping = c.Attribute("allmapping") == null ? false : c.Attribute("allmapping").Value == "true";
                //添加属性 
                foreach (var p in from p in c.Elements(xmlns + "property") select p)
                {
                    item.Propertys.Add(new PropertyInfo()
                    {
                        Name = p.Attribute("name").Value,
                        Column = p.Attribute("column") == null ? p.Attribute("name").Value : p.Attribute("column").Value,
                        Insert = p.Attribute("insert") == null ? true : p.Attribute("insert").Value == "true",
                        Update = p.Attribute("update") == null ? true : p.Attribute("update").Value == "true",
                    });
                };
                if (item.AllMapping == true)
                {
                    Type type = Type.GetType(item.Name);
                    if (type == null)
                        throw new ArgumentNullException(string.Format("类型:{0} 不存在"));
                    foreach (var p in Type.GetType(item.Name).GetProperties())
                    {
                        if (p.CanWrite == true && item.Propertys.SingleOrDefault(m => m.Name.ToLower() == p.Name.ToLower()) == null)
                        {
                            item.Propertys.Add(new PropertyInfo()
                            {
                                Name = p.Name,
                                Column = p.Name,
                                Insert = true,
                                Update = true
                            });
                        }
                    }
                }
                ClassMappings.Add(item);
            }
            //加载语句
            foreach (var q in from e in root.Elements(xmlns + "query") select e)
            {
                var item = new QueryInfo()
                {
                    Name = q.Attribute("name").Value.ToLower(),
                    Mode = q.Attribute("mode") == null ? null : q.Attribute("mode").Value,
                    Sql = q.Value
                };
                Queries.Add(item);
            }
        } 

        /// <summary>
        /// 初始化配置 
        /// </summary>
        void InitConfig()
        {
            Query.EnumAsInt = Properties.Get<bool>("enumasint", false);
            Impl.Database.WriteLog = Properties.Get<bool>("logging", true) ? _writeLog : null;
        }

        /// <summary>
        /// 根据指定路径获取相对的路径的绝对路径
        /// </summary>
        /// <param name="path"></param>
        /// <param name="relPath"></param>
        /// <returns></returns> 
        string GetAbsolutePath(string path, string relPath)
        {
            if (relPath.Contains('/'))
            {
                path = path.TrimEnd('\\');
                relPath = relPath.Replace('/', '\\');
                int p;
                while ((p = relPath.IndexOf(@"..\")) > -1)
                {
                    relPath = relPath.Substring(p + 3);
                    path = path.Substring(0, path.LastIndexOf('\\'));
                }
                path += "\\" + relPath;

                return path;
            }
            else
            {
                return relPath == "" ? path : relPath;
            }
        }

        #endregion
    }

    public class ConnectionInfo
    { 
        public DatabaseType DbType { get; set; }
        public string ConnectionString { get; set; }
    }
}